package pso.gui.slaves;

import java.util.Arrays;
import java.util.List;

import javax.swing.table.AbstractTableModel;

import pso.data.SlavePSO;
import pso.data.SlavePSO.ConstantInertiaPSO;
import pso.data.SlavePSO.ConstrictedPSO;
import pso.data.SlavePSO.DecreasingInertiaPSO;
import pso.data.SlavePSO.RandomInertiaPSO;
import pso.data.SlavePSO.SlaveClassicPSO;
import pso.implementation.PSO;

public class SlavesTableModel extends AbstractTableModel {

	private static final long serialVersionUID = 1945650959899549264L;
	private static final SlaveTableColumn[] COLUMNS = SlaveTableColumn.values();
	private List<SlavePSO> slaves = PSOSlavesInitializer.initialize();

	@Override
	public int getColumnCount() {
		return COLUMNS.length;
	}

	@Override
	public String getColumnName(int columnIndex) {
		return COLUMNS[columnIndex].toString();
	}

	@Override
	public int getRowCount() {
		return slaves.size();
	}

	@Override
	public Class<?> getColumnClass(int columnIndex) {
		SlaveTableColumn column = COLUMNS[columnIndex];
		switch (column) {
		case BEGIN_INERTIA:
		case C1:
		case C2:
		case END_INERTIA:
		case INERTIA:
			return Double.class;
		case POPULATION:
			return Integer.class;
		case TYPE:
			return PSO.class;
		}

		return null;
	}

	@Override
	public Object getValueAt(int rowIndex, int columnIndex) {
		try {
			SlavePSO slavePSO = slaves.get(rowIndex);
			SlaveTableColumn column = COLUMNS[columnIndex];
			switch (column) {
			case BEGIN_INERTIA:
				DecreasingInertiaPSO decreasingInertiaPSO = (DecreasingInertiaPSO) slavePSO;
				return decreasingInertiaPSO.getBeginInertiaWeight();
			case C1:
				return slavePSO.getCognitiveFactor();
			case C2:
				return slavePSO.getSocialFactor();
			case END_INERTIA:
				DecreasingInertiaPSO decrInertiaPSO = (DecreasingInertiaPSO) slavePSO;
				return decrInertiaPSO.getEndInertiaWeight();
			case INERTIA:
				ConstantInertiaPSO constInertiaPSO = (ConstantInertiaPSO) slavePSO;
				return constInertiaPSO.getInertiaWeight();
			case POPULATION:
				return slavePSO.getSwarmSize();
			case TYPE:
				if (slavePSO instanceof ConstantInertiaPSO) {
					return PSO.CONSTANT_INERTIA;
				} else if (slavePSO instanceof ConstrictedPSO) {
					return PSO.CONSTRICTION;
				} else if (slavePSO instanceof DecreasingInertiaPSO) {
					return PSO.DECREASING_INERTIA;
				} else if (slavePSO instanceof RandomInertiaPSO) {
					return PSO.RANDOM_INERTIA;
				} else if (slavePSO instanceof SlaveClassicPSO) {
					return PSO.CLASSIC;
				}
			}
		} catch (ClassCastException e) {
			return null;
		}
		return null;
	}

	@Override
	public boolean isCellEditable(int rowIndex, int columnIndex) {
		if (columnIndex < 4) {
			return true;
		}
		SlavePSO slavePSO = slaves.get(rowIndex);

		SlaveTableColumn column = COLUMNS[columnIndex];
		if (slavePSO instanceof ConstantInertiaPSO) {
			return SlaveTableColumn.INERTIA == column;
		} else if (slavePSO instanceof DecreasingInertiaPSO) {
			return SlaveTableColumn.BEGIN_INERTIA == column
					|| SlaveTableColumn.END_INERTIA == column;
		}
		return false;
	}

	@Override
	public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
		SlavePSO slavePSO = slaves.get(rowIndex);
		SlaveTableColumn column = COLUMNS[columnIndex];

		switch (column) {
		case BEGIN_INERTIA:
			DecreasingInertiaPSO decreasingInertiaPSO = (DecreasingInertiaPSO) slavePSO;
			decreasingInertiaPSO.setBeginInertiaWeight((double) aValue);
			break;
		case C1:
			slavePSO.setCognitiveFactor((double) aValue);
			break;
		case C2:
			slavePSO.setSocialFactor((double) aValue);
			break;
		case END_INERTIA:
			DecreasingInertiaPSO decrInertiaPSO = (DecreasingInertiaPSO) slavePSO;
			decrInertiaPSO.setEndInertiaWeight((double) aValue);
			break;
		case INERTIA:
			ConstantInertiaPSO constInertiaPSO = (ConstantInertiaPSO) slavePSO;
			constInertiaPSO.setInertiaWeight((double) aValue);
			break;
		case POPULATION:
			slavePSO.setSwarmSize((int) aValue);
			break;
		case TYPE:
			SlavePSO newSlavePSO = null;
			switch ((PSO) aValue) {
			case CLASSIC:
				newSlavePSO = new SlaveClassicPSO(slavePSO);
				break;
			case CONSTANT_INERTIA:
				newSlavePSO = new ConstantInertiaPSO(slavePSO);
				break;
			case CONSTRICTION:
				newSlavePSO = new ConstrictedPSO(slavePSO);
				break;
			case DECREASING_INERTIA:
				newSlavePSO = new DecreasingInertiaPSO(slavePSO);
				break;
			case RANDOM_INERTIA:
				newSlavePSO = new RandomInertiaPSO(slavePSO);
				break;
			}
			if (newSlavePSO.getClass() != slavePSO.getClass()) {
				slaves.set(rowIndex, newSlavePSO);
			}
		}

		fireTableRowsUpdated(rowIndex, rowIndex);
	}

	public int appendRow() {
		int size = slaves.size();
		slaves.add(new SlaveClassicPSO());
		fireTableRowsInserted(size, size);
		return size;
	}

	public void removeRow(int rowIndex) {
		slaves.remove(rowIndex);
		fireTableRowsDeleted(rowIndex, rowIndex);
	}

	public void removeRows(int[] rows) {
		int length = rows.length;
		int[] rowsCopy = Arrays.copyOf(rows, length);
		Arrays.sort(rowsCopy);
		for (int i = length - 1; i >= 0; i--) {
			removeRow(rowsCopy[i]);
		}
	}

	public SlavePSO getElementAt(int rowIndex) {
		return slaves.get(rowIndex);
	}
}
